package asteroids.model.programs.statements.ProgramStatements;

import asteroids.model.programs.Program;
import asteroids.model.programs.expressions.Expression;
import asteroids.model.programs.expressions.BasicExpressions.BooleanLiteral;
import asteroids.model.programs.statements.ProgramStatement;
import asteroids.model.programs.statements.Statement;
import asteroids.model.programs.types.Type;

/**
 * A class representing a While-statement. This class is a subclass of the
 * Statement-class.
 * 
 * A While-statement has a condition that can be evaluated to a boolean value.
 * As long as this boolean value results to true, the body of the
 * While-statement will be executed.
 * 
 * @author Michiel De Cuyper
 * @version 09.05
 * 
 */
public class WhileStatement extends ProgramStatement {

	/**
	 * Constructor for the WhileStatement-class
	 * 
	 * @param line
	 *            The line on which this if-statement is found
	 *            Gets passed to the Statement-class
	 * @param column
	 *            The column in which this if-statement is found
	 *            Gets passed to the Statement-class
	 * @param condition
	 *            A boolean condition that represents the condition of this
	 *            While-statement
	 * @param body
	 *            An expression representing the body of this While-statement
	 */
	public WhileStatement(int line, int column, Expression condition, Statement body) {
		super(line, column);
		this.setCondition(condition);
		this.setBody((SequenceStatement) body);
	}

	public void setProgram(Program program) {
		super.setProgram(program);
		this.getBody().setProgram(program);
	}

	/**
	 * Executes this while-statement, by evaluating the condition each
	 * iteration.
	 * As long as the condition evaluates to true, the body is executed.
	 * 
	 * @effect ...
	 *         | while(condition.getValue() == true) body.execute()
	 */
	@Override
	public void execute() {
		while (((BooleanLiteral) this.getCondition().getResult()).getValue()) {
			this.getBody().execute();
		}
	}


	/**
	 * Sets the condition of this while-statement to a given condition.
	 * 
	 * @param condition
	 *            The condition to be set
	 * @post ...
	 *       | (new this).getCondition == condition
	 */
	public void setCondition(Expression condition) {
		this.condition = condition;
	}

	/**
	 * Returns the condition of this while-statement
	 * 
	 * @return ...
	 *         | return this.condition;
	 */
	public Expression getCondition() {
		return this.condition;
	}

	/**
	 * An Expression-variable, storing the condition of this while-statement.
	 */
	private Expression condition;

	/**
	 * Sets the body of this while-statement to a given body.
	 * 
	 * @param body
	 *            The body to be set
	 * @post ...
	 *       | (new this).getBody == body
	 */
	public void setBody(SequenceStatement body) {
		this.body = body;
	}

	/**
	 * Returns the body of this while-statement
	 * 
	 * @return ...
	 *         | return this.body;
	 */
	public SequenceStatement getBody() {
		return this.body;
	}

	/**
	 * A Statement-variable, storing the body of this while-statement.
	 */
	private SequenceStatement body;

	@Override
	public Expression getExpression() {
		return condition;
	}

	@Override
	public String typeCheckStatement(String errorMessage) {
		String expressionMessage = getExpression().typeCheckExpression(errorMessage);
		
		String conditionMessage = null;
		if(getExpression().getType() != Type.BOOLEAN){
			conditionMessage = "Loop condition at line " + getLine() + " and column " +
								getColumn() + " is of type " + getExpression().getType() +
								" and should be of type " + Type.BOOLEAN + "\n";
		}
		
		if(expressionMessage != null && conditionMessage != null) expressionMessage += conditionMessage;
		if(expressionMessage == null) expressionMessage = conditionMessage;
		return expressionMessage;
	}
}
